// T称为泛型变量，只表示类型，不表示值

// 泛型函数 ，泛型函数的类型与非泛型函数的类型没什么不同，只是有一个类型参数在最前面，像函数声明一样：
function abc<T>(arg: T): T {
    // console.log(arg.length); // 编译器报错，因为arg类型不定，可能是数字number，number没有length属性
    return arg
}

//写法1： 根据传入的值的类型，自动推断出类型，把T设为它的类型
const a = abc('mysy')
const b = abc(3)
//写法2： 手动明确传入的类型
const c = abc<string>('haha')

let myabc: <T>(arg: T) => T = abc;

// T[]等于Array<T>
function ab<T>(arg: T[]): T[] {
    return arg
}
// function ab<T>(arg:Array<T>):Array<T>{}

//====================================↓   ↓   ↓   ↓====================================
// 使用带有调用签名的对象字面量来定义泛型函数
function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: { <T>(arg: T): T } = identity;

//泛型接口 (把上面例子里的对象字面量拿出来做为一个接口)
// 写法1
interface Gd {
    <T>(arg: T): T
}

function test<T>(arg: T): T {
    return arg;
}
let mytest: Gd = test;
mytest('对')//√
mytest(345)//√

// 写法2: 
interface Gd2<T> {
    (arg: T): T
}
function test2<T>(arg: T): T {
    return arg
}
let mytest2: Gd2<number> = test2;
// mytest2('手动') //x
mytest2(15) //√
//====================================↑   ↑   ↑   ↑====================================


//====================================↓   ↓   ↓   ↓====================================
// 泛型类
// 类有两部分：静态部分和实例部分。 泛型类指的是实例部分的类型
// class GenericNumber<T> {
//     zeroValue: T;
//     add: (x: T, y: T) => T;
// }

// let myGenericNumber = new GenericNumber<number>();
// myGenericNumber.zeroValue = 0;
// myGenericNumber.add = function(x, y) { return x + y; };

// let stringNumeric = new GenericNumber<string>();
// stringNumeric.zeroValue = "";
// stringNumeric.add = function(x, y) { return x + y; };

// console.log(stringNumeric.add(stringNumeric.zeroValue, "test"));
//====================================↑   ↑   ↑   ↑====================================



//====================================↓   ↓   ↓   ↓====================================
// 泛型约束
interface Lengthwise {
    length: number;
}

// 约束了传入参数必须要有length属性
function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}
loggingIdentity('web'); //ok
// loggingIdentity(33) // err

// 在泛型约束中使用类型参数
// function getProperty(obj:T,key:k){
//     return obj[key]
// }
// let x = { a: 1, b: 2, c: 3, d: 4 };
// getProperty(x, "a"); // okay
// getProperty(x, "m"); //err
 
//====================================↑   ↑   ↑   ↑====================================

class Animal {
    name: string;
}
class Dog extends Animal {
    breed: string;
}

// 错误：使用'string'索引，有时会得到Animal!
// interface NotOkay {
    // [x: number]: Animal;
    // [x: string]: Dog;
// }

